use crate::error::CompileErrorKind;
use std::{ str::FromStr};

#[derive(Debug,Clone, Copy, PartialEq, Eq)]
pub struct Imm8Type(u8);

impl FromStr for Imm8Type {
    type Err = CompileErrorKind;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // todo!()
        let val = s
            .parse::<u8>()
            .map_err(|_err| CompileErrorKind::OperandParseError)?;
        Ok(Imm8Type(val))
    }
}
#[derive(Debug)]
pub enum ParamType {
    I(Imm8Type),
    R(RegKind),
}

impl FromStr for ParamType {
    type Err = CompileErrorKind;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let tmp_bytes = s.as_bytes();
        let proc = tmp_bytes[0] as char;
        match proc {
            'R' => s.parse().map(|v| ParamType::R(v)),
            '#' => s[1..].parse().map(|v| ParamType::I(v)),
            _ => Err(CompileErrorKind::OperandParseError),
        }
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegKind {
    R0 = 0,
    R1 = 1,
    R2 = 2,
    R3 = 3,
    R4 = 4,
    R5 = 5,
    R6 = 6,
    R7 = 7,
    R8 = 8,
    R9 = 9,
    R10 = 10,
    R11 = 11,
    R12 = 12,
    R13 = 13,
    R14 = 14,
    R15 = 15,
}

impl From<RegKind> for u16 {
    fn from(kind: RegKind) -> Self {
        kind as u16
    }
}

// 指令解析器
// use error::CompileErrorKind;

#[derive(Debug,Clone,Copy, PartialEq, Eq)]
pub enum Command {
    NOP,
    BRnzp(Imm8Type),                // nzp imm8
    CMP(RegKind, RegKind),          // RS RT
    ADD(RegKind, RegKind, RegKind), // RD RS RT
    SUB(RegKind, RegKind, RegKind), // RD RS RT
    MUL(RegKind, RegKind, RegKind), // RD RS RT
    DIV(RegKind, RegKind, RegKind), // RD RS RT
    LDR(RegKind, RegKind),          // RD RS
    STR(RegKind, RegKind),          // RS RT
    CONST(RegKind, Imm8Type),       // RD IMM8
    RET,
}

fn params_to_vec<'a>(params: Option<&'a &'a str>) -> Vec<&'a str> {
    let mut result: Vec<&str> = Vec::new();
    match params {
        Some(str) => {
            let str_split: Vec<&str> = str.split(",").collect();
            for word in str_split {
                let trimmed_word = word.trim();
                if !trimmed_word.is_empty() {
                    result.push(trimmed_word);
                }
            }
        }
        None => {}
    };

    result
}
// 字符串转 imm8
fn param_imm8(os: Option<&&str>) -> Result<Imm8Type, CompileErrorKind> {
    let s = match os {
        Some(str) => str,
        None => return Err(CompileErrorKind::OperandParseError),
    };

    let ret: Imm8Type = match s.parse::<ParamType>()? {
        ParamType::I(val) => val,
        _ => {
            return Err(CompileErrorKind::OperandParseError);
        }
    };
    Ok(ret)
}

fn param_reg(os: Option<&&str>) -> Result<RegKind, CompileErrorKind> {
    let s = match os {
        Some(str) => str,
        None => return Err(CompileErrorKind::OperandParseError),
    };

    let ret = match s.parse::<ParamType>()? {
        ParamType::R(val) => val,
        _ => {
            return Err(CompileErrorKind::OperandParseError);
        }
    };
    Ok(ret)
}

fn to_code_rd_rs_rt(op: u16, rd: &RegKind, rs: &RegKind, rt: &RegKind) -> u16 {
    let rd_code: u16 = *rd as u16 & 0xf;
    let rs_code: u16 = *rs as u16 & 0xf;
    let rt_code = *rt as u16 & 0xf;

    op << 12 | rd_code << 8 | rs_code << 4 | rt_code
}

fn to_code_rd_rs(op: u16, rd: &RegKind, rs: &RegKind) -> u16 {
    let rd_code: u16 = *rd as u16 & 0xf;
    let rs_code: u16 = *rs as u16 & 0xf;

    op << 12 | rd_code << 8 | rs_code << 4
}

fn to_code_rs_rt(op: u16, rs: &RegKind, rt: &RegKind) -> u16 {
    let rs_code: u16 = *rs as u16 & 0xf;
    let rt_code = *rt as u16 & 0xf;

    op << 12 | rs_code << 4 | rt_code
}

fn to_code_rd_imm8(op: u16, rd: &RegKind, imm8: &Imm8Type) -> u16 {
    let rd_code: u16 = *rd as u16 & 0xf;

    let imm8: u16 = imm8.0 as u16 & 0xff;
    op << 12 | rd_code << 8 | imm8
}

fn to_code_imm8(op: u16, imm8: &Imm8Type) -> u16 {
    let imm8: u16 = imm8.0 as u16 & 0xff;
    op << 12 | imm8
}

impl Command {
    pub fn parse(op: &str, params: Option<&&str>) -> Result<Command, CompileErrorKind> {
        let params = params_to_vec(params);

        // 使用空格 拆分 指令
        // 再用 ，拆分参数

        let ret = match op {
            "NOP" => Command::NOP,
            "BRnzp" => {
                // let param1 = params.get(0);
                let imm8 = param_imm8(params.get(0))?;
                Command::BRnzp(imm8)
            }
            "CMP" => {
                let rs = param_reg(params.get(0))?;
                let rt = param_reg(params.get(1))?;
                Command::CMP(rs, rt)
            }
            "ADD" => {
                let rd = param_reg(params.get(0))?;
                let rs = param_reg(params.get(1))?;
                let rt = param_reg(params.get(2))?;
                Command::ADD(rd, rs, rt)
            }
            "SUB" => {
                let rd = param_reg(params.get(0))?;
                let rs = param_reg(params.get(1))?;
                let rt = param_reg(params.get(2))?;
                Command::SUB(rd, rs, rt)
            }
            "MUL" => {
                let rd = param_reg(params.get(0))?;
                let rs = param_reg(params.get(1))?;
                let rt = param_reg(params.get(2))?;
                Command::MUL(rd, rs, rt)
            }
            "DIV" => {
                let rd = param_reg(params.get(0))?;
                let rs = param_reg(params.get(1))?;
                let rt = param_reg(params.get(2))?;
                Command::DIV(rd, rs, rt)
            }
            "LDR" => {
                let rd: RegKind = param_reg(params.get(0))?;
                let rs = param_reg(params.get(1))?;
                Command::LDR(rd, rs)
            }
            "STR" => {
                let rs: RegKind = param_reg(params.get(0))?;
                let rt = param_reg(params.get(1))?;
                Command::STR(rs, rt)
            }
            "CONST" => {
                let rd: RegKind = param_reg(params.get(0))?;
                let imm8 = param_imm8(params.get(1))?;
                Command::CONST(rd, imm8)
            }
            "RET" => Command::RET,
            _ => return Err(CompileErrorKind::OperandParseError),
        };

        // Ok(Command::NOP)
        Ok(ret)
    }

    pub fn to_code(&self) -> Result<u16, CompileErrorKind> {
        let code = match &self {
            Command::NOP => 0u16,
            Command::BRnzp(imm8) =>to_code_imm8(1, imm8),
            Command::CMP(rs, rt) => to_code_rs_rt(2, rs, rt),
            Command::ADD(rd, rs, rt) => to_code_rd_rs_rt(3, rd, rs, rt),
            Command::SUB(rd, rs, rt) => to_code_rd_rs_rt(4, rd, rs, rt),
            Command::MUL(rd, rs, rt) => to_code_rd_rs_rt(5, rd, rs, rt),
            Command::DIV(rd, rs, rt) => to_code_rd_rs_rt(6, rd, rs, rt),
            Command::LDR(rd,rs ) =>to_code_rd_rs(7, rd, rs),
            Command::STR(rd ,rt ) => to_code_rs_rt(8, rd, rt),
            Command::CONST(rd , imm8 ) => to_code_rd_imm8(9, rd, imm8),
            Command::RET => 0xf000,
    
        };

        Ok(code)
    }
}

impl FromStr for RegKind {
    type Err = CompileErrorKind;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "R0" => Ok(RegKind::R0),
            "R1" => Ok(RegKind::R1),
            "R2" => Ok(RegKind::R2),
            "R3" => Ok(RegKind::R3),
            "R4" => Ok(RegKind::R4),
            "R5" => Ok(RegKind::R5),
            "R6" => Ok(RegKind::R6),
            "R7" => Ok(RegKind::R7),
            "R8" => Ok(RegKind::R8),
            "R9" => Ok(RegKind::R9),
            "R10" => Ok(RegKind::R10),
            "R11" => Ok(RegKind::R11),
            "R12" => Ok(RegKind::R12),
            "R13" => Ok(RegKind::R13),
            "R14" => Ok(RegKind::R14),
            "R15" => Ok(RegKind::R15),
            _ => Err(CompileErrorKind::NameNotExist {
                name: s.to_string(),
            }),
        }
    }
}

#[test]
fn proc_command_test() {
    let line = "MUL R0, R13, R14";
    // let line  = "#11";
    let stream: Vec<&str> = line.splitn(2, " ").collect();

    let op = stream[0];
    let params = stream.get(1);

    let cmd_target = Command::parse(op, params).unwrap();

    let cmd_truth = Command::MUL(RegKind::R0, RegKind::R13, RegKind::R14);

    assert_eq!(cmd_target, cmd_truth);
}

#[test]
fn test_reg_int() {
    let test_reg = RegKind::R1;

    let val: u8 = test_reg as u8 + 1;

    println!("{:?} {:?}", val, val);
}
